package com.qining618.common.interceptor;

import com.qining618.common.contants.HeaderConstants;
import com.qining618.common.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;
import org.springframework.http.MediaType;
import org.springframework.web.util.ContentCachingRequestWrapper;
import org.springframework.web.util.ContentCachingResponseWrapper;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;

import static com.qining618.common.contants.SysConstants.TRACE_ID;

/**
 * @author flt
 * @date 2019-08-23
 */
@Slf4j
public class LogFilter implements Filter {

    private static final String REQUEST_PREFIX = "\n>>>>>>>>>>>>>>Request: BEGIN>>>>>>>>>>>>>>";
    private static final String REQUEST_SUFFIX = "\n>>>>>>>>>>>>>>Request: END>>>>>>>>>>>>>>";

    private static final String RESPONSE_PREFIX = "\n<<<<<<<<<<<<<<Response: BEGIN<<<<<<<<<<<<<<";
    private static final String RESPONSE_SUFFIX = "\n<<<<<<<<<<<<<<Response: END<<<<<<<<<<<<<<";

    private String[] excludedUris;

    @Override
    public void init(FilterConfig config) throws ServletException {
        excludedUris = config.getInitParameter("excludedUri").split(",");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;

        String uri = request.getRequestURI();
        if (isExcludedUri(uri)){
            chain.doFilter(request, response);
            return;
        }

        ContentCachingRequestWrapper wrapperRequest = new ContentCachingRequestWrapper(request);
        ContentCachingResponseWrapper wrapperResponse = new ContentCachingResponseWrapper(response);

        long beginTime = System.currentTimeMillis();
        try {
            chain.doFilter(wrapperRequest, wrapperResponse);
        } finally {
            long useTime = System.currentTimeMillis() - beginTime;

            StringBuilder msg = new StringBuilder();
            msg.append(REQUEST_PREFIX);
            msg.append("\nrequest id=").append(MDC.get(TRACE_ID)).append(";");
            msg.append("\nauthorization=").append(request.getHeader(HeaderConstants.AUTHORIZATION));
            msg.append("\ncall-source=").append(request.getHeader(HeaderConstants.CALL_SOURCE));
            msg.append("\napp-version=").append(request.getHeader(HeaderConstants.APP_VERSION));
            if (request.getMethod() != null) {
                msg.append("\nmethod=").append(request.getMethod()).append("; ");
            }
            if (request.getContentType() != null) {
                msg.append("\ncontent-type=").append(request.getContentType()).append("; ");
            }
            msg.append("\nuri=").append(request.getRequestURI());
            if (request.getQueryString() != null) {
                msg.append('?').append(request.getQueryString());
            }
            msg.append(";");
            if (!isBinaryContent(request) && !isMultipart(request)) {
                Charset charset = Charset.forName(StringUtils.defaultString(wrapperRequest.getCharacterEncoding(), "UTF-8"));
                msg.append("\npayload=").append(new String(wrapperRequest.getContentAsByteArray(), charset));
            }
            msg.append(REQUEST_SUFFIX);
            msg.append(RESPONSE_PREFIX);
            msg.append("\nrequest id=").append(MDC.get(TRACE_ID));
            if (response.getContentType() != null && response.getContentType().contains(MediaType.APPLICATION_JSON_VALUE)) {
                msg.append("\npayload=").append(new String(wrapperResponse.getContentAsByteArray(), StandardCharsets.UTF_8));
            }
            msg.append(";\nuseTime=").append(useTime);
            msg.append(RESPONSE_SUFFIX);
            log.info(msg.toString());
            wrapperResponse.copyBodyToResponse();
        }
    }

    private boolean isBinaryContent(final HttpServletRequest request) {
        if (request.getContentType() == null) {
            return false;
        }
        return request.getContentType().startsWith("image")
                || request.getContentType().startsWith("video")
                || request.getContentType().startsWith("audio")
                || request.getContentType().contains("stream");
    }

    private boolean isMultipart(final HttpServletRequest request) {
        return request.getContentType() != null && request.getContentType().startsWith("multipart/form-data");
    }

    private boolean isExcludedUri(String uri) {
        if (excludedUris == null || excludedUris.length <= 0) {
            return false;
        }
        for (String ex : excludedUris) {
            uri = uri.trim();
            ex = ex.trim();
            String regex = ex.toLowerCase().replace("*",".*");
            if (uri.toLowerCase().matches(regex)) {
                return true;
            }
        }
        return false;
    }
}
